/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lvyh.lightframe.tracer.plugin.springcloud;

import com.lvyh.lightframe.tracer.core.SimpleSpan;
import com.lvyh.lightframe.tracer.core.SimpleSpanContext;
import com.lvyh.lightframe.tracer.core.common.TraceContants;
import com.lvyh.lightframe.tracer.core.context.SimpleTraceContextHolder;
import com.lvyh.lightframe.tracer.core.util.StringUtils;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

public class FeignClientRequestInterceptor implements RequestInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(FeignClientRequestInterceptor.class);

    @Override
    public void apply(RequestTemplate template) {
        logger.info("[FeignClientRequestInterceptor] entry in feign interceptor.");
        Map<String, String> headers = getHeaders(getHttpServletRequest());
        for (String headerName : headers.keySet()) {
            template.header(headerName, headers.get(headerName));
            logger.info("Client fill request header:" + headerName + ":" + headers.get(headerName));
        }

        SimpleSpan currentSpan = SimpleTraceContextHolder.getSimpleTraceContext().getCurrentSpan();
        if (currentSpan != null) {
            SimpleSpanContext spanContext = currentSpan.getSimpleSpanContext();
            //Tracing Context
            template.header(TraceContants.TRACE_ID_KEY_HEAD, encodedValue(spanContext.getTraceId()));
            template.header(TraceContants.SPAN_ID_KEY_HEAD, encodedValue(spanContext.getSpanId()));
            template.header(TraceContants.PARENT_SPAN_ID_KEY_HEAD, encodedValue(spanContext.getParentId()));
            template.header(TraceContants.SPAN_ID_KEY_HEAD, encodedValue(spanContext.getSpanId()));
            template.header(TraceContants.SAMPLED_KEY_HEAD, encodedValue(String.valueOf(spanContext.isSampled())));

            for (Map.Entry<String, String> entry : spanContext.getBaggage().entrySet()) {
                String key = TraceContants.BAGGAGE_KEY_PREFIX + StringUtils.escapePercentEqualAnd(entry.getKey());
                String value = encodedValue(StringUtils.escapePercentEqualAnd(entry.getValue()));
                template.header(key, value);
            }
        }
    }

    protected String encodedValue(String value) {
        if (StringUtils.isBlank(value)) {
            return StringUtils.EMPTY_STRING;
        }
        try {
            return URLEncoder.encode(value, TraceContants.DEFAULT_UTF8_ENCODING);
        } catch (UnsupportedEncodingException e) {
            // not much we can do, try raw value
            return value;
        }
    }

    private HttpServletRequest getHttpServletRequest() {
        try {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> map = new LinkedHashMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        while (enumeration.hasMoreElements()) {
            String key = enumeration.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }
}
